package com.pkk.components.encryption.util;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;


import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * @author peikunkun
 * @version V1.0
 * @Title: myknow
 * @Package com.pkk.encrytion.util
 * @Description: <>
 * @date 2018/5/14 14:47
 */
public class RSAUtil {

  public static final String CHARSET_UTF8 = "UTF-8";

  /**
   * 签名标准名称
   */
  public static final String KEY_SHA1WITHRSA = "SHA1WithRSA";
  /**
   * 保存一对公私钥
   */
  public static final ThreadLocal<Map<String, String>> priAndPubKey = new ThreadLocal<>();
  /**
   * 密钥算法
   */
  private static final String KEY_ALGORITHM = "RSA";
  /**
   * 签名标准名称
   */
  private static final String KEY_MD5WITHRSA = "MD5withRSA";
  /**
   * 公钥
   */
  private static final String KEY_PUBLICKEY = "publicKey";
  /**
   * 私钥
   */
  private static final String KEY_PRIVATEKEY = "privateKey";


  public RSAUtil() {
    Map<String, Object> map = initKey();
    HashMap<String, String> priAndPubMap = new HashMap<>(2);
    priAndPubMap.put(KEY_PUBLICKEY, new BASE64Encoder().encodeBuffer((byte[]) map.get(KEY_PUBLICKEY)));
    priAndPubMap.put(KEY_PRIVATEKEY, new BASE64Encoder().encodeBuffer((byte[]) map.get(KEY_PRIVATEKEY)));
    priAndPubKey.set(priAndPubMap);
  }

  private static final Map<String, Object> initKey() {
    KeyPairGenerator keyPairGenerator = null;
    try {
      keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }

    keyPairGenerator.initialize(1024);
    KeyPair keyPair = keyPairGenerator.generateKeyPair();

    PublicKey publicKey = keyPair.getPublic();
    PrivateKey privateKey = keyPair.getPrivate();

    HashMap<String, Object> map = new HashMap<>(2);
    map.put(KEY_PUBLICKEY, publicKey.getEncoded());
    map.put(KEY_PRIVATEKEY, privateKey.getEncoded());
    return map;
  }

  /**
   * @param
   * @return 公钥
   * @Description: <获取公钥>
   * @author peikunkun
   * @date 2018/5/14 15:10
   * @version V1.0
   */
  public final static String getPublicKey() {
    byte[] publicKey = (byte[]) initKey().get(KEY_PUBLICKEY);
    return new BASE64Encoder().encodeBuffer(publicKey);
  }

  /**
   * @param signData 签名的数据
   * @param privateKey 密钥（私钥）
   * @return 签名之后的字符串
   * @Description: <使用MD5WithRsa进行数据签名>
   * @author peikunkun
   * @date 2018/5/14 15:22
   * @version V1.0
   */
  public static final String signMd5WithRsa(String signData, String privateKey) {
    return signMd5WithRsa(signData, privateKey, CHARSET_UTF8);
  }

  /**
   * @param signData 签名的数据
   * @param privateKey 密钥（私钥）
   * @param charSet 签名编码-默认UTF-8
   * @return 签名之后的字符串
   * @Description: <使用MD5WithRsa进行数据签名>
   * @author peikunkun
   * @date 2018/5/14 15:22
   * @version V1.0
   */
  public static final String signMd5WithRsa(String signData, String privateKey, String charSet) {
    if (StringUtils.isBlank(charSet)) {
      charSet = CHARSET_UTF8;
    }
    try {
      PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(privateKey));
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      PrivateKey privateKey1 = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

      //进行签名
      Signature signature = Signature.getInstance(KEY_MD5WITHRSA);
      signature.initSign(privateKey1);
      signature.update(signData.getBytes(charSet));

      //得到sign
      byte[] sign = signature.sign();
      return new BASE64Encoder().encodeBuffer(sign);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
      return null;
    } catch (IOException e) {
      e.printStackTrace();
      return null;
    } catch (InvalidKeySpecException e) {
      e.printStackTrace();
      return null;
    } catch (InvalidKeyException e) {
      e.printStackTrace();
      return null;
    } catch (SignatureException e) {
      e.printStackTrace();
      return null;
    }

  }

  /**
   * @param signData 签名的数据
   * @param privateKey 密钥（私钥）
   * @return 签名之后的字符串
   * @Description: <使用sha1WithRsa进行数据签名>
   * @author peikunkun
   * @date 2018/5/14 15:22
   * @version V1.0
   */
  public static final String signSHA1WithRsa(String signData, String privateKey) {
    return signSHA1WithRsa(signData, privateKey, CHARSET_UTF8);
  }

  /**
   * @param signData 签名的数据
   * @param privateKey 密钥（私钥）
   * @param charSet 签名编码-默认UTF-8
   * @return 签名之后的字符串
   * @Description: <使用sha1WithRsa进行数据签名>
   * @author peikunkun
   * @date 2018/5/14 15:22
   * @version V1.0
   */
  public static final String signSHA1WithRsa(String signData, String privateKey, String charSet) {

    if (StringUtils.isBlank(charSet)) {
      charSet = CHARSET_UTF8;
    }
    try {
      PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(privateKey));
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      PrivateKey privateKey1 = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

      Signature signature = Signature.getInstance(KEY_SHA1WITHRSA);
      signature.initSign(privateKey1);
      signature.update(signData.getBytes(charSet));

      //获取签名数据
      byte[] sign = signature.sign();
      return new BASE64Encoder().encodeBuffer(sign);
    } catch (IOException e) {
      e.printStackTrace();
      return null;
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
      return null;
    } catch (InvalidKeySpecException e) {
      e.printStackTrace();
      return null;
    } catch (InvalidKeyException e) {
      e.printStackTrace();
      return null;
    } catch (SignatureException e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * @param
   * @return 验证结果
   * @Description: <验证结果>
   * @author peikunkun
   * @date 2018/5/14 16:44
   * @version V1.0
   */
  public static final boolean verifyMD5WithRsa(String contentData, String sign, String publicKey) {
    return verifyMD5WithRsa(contentData, sign, publicKey, CHARSET_UTF8);
  }

  /**
   * @param
   * @return 验证结果
   * @Description: <验证结果>
   * @author peikunkun
   * @date 2018/5/14 16:44
   * @version V1.0
   */
  private static boolean verifyMD5WithRsa(String contentData, String sign, String publicKey, String charSet) {
    if (StringUtils.isBlank(charSet)) {
      charSet = CHARSET_UTF8;
    }
    try {
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(publicKey));
      PublicKey publicKey1 = keyFactory.generatePublic(x509EncodedKeySpec);

      Signature signature = Signature.getInstance(KEY_MD5WITHRSA);
      signature.initVerify(publicKey1);
      signature.update(contentData.getBytes(charSet));
      boolean verifyResult = signature.verify(new BASE64Decoder().decodeBuffer(sign));
      return verifyResult;
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
      return false;
    } catch (InvalidKeySpecException e) {
      e.printStackTrace();
      return false;
    } catch (IOException e) {
      e.printStackTrace();
      return false;
    } catch (InvalidKeyException e) {
      e.printStackTrace();
      return false;
    } catch (SignatureException e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * @param
   * @return 验证签名结果
   * @Description: <验签>
   * @author peikunkun
   * @date 2018/5/14 16:36
   * @version V1.0
   */
  public static final boolean verifySHA1WithRsa(String contentData, String sign, String publicKey) {
    return verifySHA1WithRsa(contentData, sign, publicKey, CHARSET_UTF8);
  }

  /**
   * @param
   * @return 验证签名结果
   * @Description: <验签>
   * @author peikunkun
   * @date 2018/5/14 16:36
   * @version V1.0
   */
  public static final boolean verifySHA1WithRsa(String contentData, String sign, String publicKey, String charSet) {
    if (StringUtils.isBlank(charSet)) {
      charSet = CHARSET_UTF8;
    }
    try {
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
      byte[] publicKey1 = new BASE64Decoder().decodeBuffer(publicKey);
      X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey1);
      PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec);

      //验签
      Signature signature = Signature.getInstance(KEY_SHA1WITHRSA);
      signature.initVerify(publicKey2);
      signature.update(contentData.getBytes(charSet));
      boolean verifyResult = signature.verify(new BASE64Decoder().decodeBuffer(sign));
      return verifyResult;
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
      return false;
    } catch (InvalidKeySpecException e) {
      e.printStackTrace();
      return false;
    } catch (IOException e) {
      e.printStackTrace();
      return false;
    } catch (InvalidKeyException e) {
      e.printStackTrace();
      return false;
    } catch (SignatureException e) {
      e.printStackTrace();
      return false;
    }

  }

  /**
   * @param
   * @return 私钥
   * @Description: <获取私钥>
   * @author peikunkun
   * @date 2018/5/14 15:10
   * @version V1.0
   */
  public final static String getPrivateKey() {
    byte[] privateKey = (byte[]) initKey().get(KEY_PRIVATEKEY);
    return new BASE64Encoder().encodeBuffer(privateKey);
  }

  public static void main(String[] args) {
    System.out.println(getPublicKey());
    System.out.println(getPrivateKey());

    String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAICb0YonGz1TgWMXeJeCZjiQ2raK\n" +
        "ufTONWel3JMoytoRux8RmQGfwHrVnaTX/Uep0pK08LYt+S56snhgb0g/bMywYkTL7+5z6gJ+utRf\n" +
        "p17I/jEx4Xt3CdVs9rbAmrLj52sNL9vEMmIUHxqBHXdDJ+NYUmH1nKbScENrUZ02hWitAgMBAAEC\n" +
        "gYA6PFSJ5mNjRajVIg+tK5L2EkG3XuINhEKSiHx09ofEbzr4OpO6NIiIFqqntlYuWSvM2roVBIRN\n" +
        "E6o08BXz9SRidulkQRFGCA+PKa2VGBmplBPLDQ5QWi3Bn7heiqpDcQwNMySCKzF73ewsmD9ZxfjA\n" +
        "5KMNuCGElTsVQD8oJxTKAQJBAMDqyZqqshdRGBc2jfGXIC0GdA79aeR0nma2FmabY42LcOwWuMBs\n" +
        "DLBVhIsIpEr0U1/cAUT42QXRihvaaiFO3o0CQQCqqbqpi5EY0FbG61rpd7as1E8FNDbEC9knxmvO\n" +
        "p7cr/YPH2ddqKDNfO3fnJAYYlKwL48agtGUnRSjOGV/RXbqhAkBJWjzT4Wmb8gq98NFh9fuXAygn\n" +
        "9vVHl4wPM9cqppTg4g9KxYz0JWFhvvKJxvxpP6NgBJA7AH/MdZMU6iSGi62VAkB24jdCphOUWoUo\n" +
        "mojA/ADH0jmdoWrJFIvYNlOhKJnjzfz/udNigtCOdWmoHSMV+nMC8U/LYP7Z5UZwk6J4wTchAkAw\n" +
        "ZRfK8AE8YEszNAQ0nsOdRm7POHC9mbdpfqXUrS8OUE3KrAncIeArQH7fxH9DPFDr0jP3Y9SeNjNq\n" +
        "fOL8xh5T";
    String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAm9GKJxs9U4FjF3iXgmY4kNq2irn0zjVnpdyT\n" +
        "KMraEbsfEZkBn8B61Z2k1/1HqdKStPC2LfkuerJ4YG9IP2zMsGJEy+/uc+oCfrrUX6deyP4xMeF7\n" +
        "dwnVbPa2wJqy4+drDS/bxDJiFB8agR13QyfjWFJh9Zym0nBDa1GdNoVorQIDAQAB";
    String contentData = "abcdefg";
    String sign = signMd5WithRsa(contentData, privateKey);
    System.out.println("签名的数据为:" + sign);
    boolean verifyResult = verifyMD5WithRsa(contentData, sign, publicKey);
    System.out.println("验签结果:" + verifyResult);

    System.out.println("欢迎使用单元测试方法【Administrator_348()】");
    System.out.println("此方法测试描述：【获取rsa公私钥】");
    RSAUtil rsaUtil = new RSAUtil();
    String publicKey1 = priAndPubKey.get().get(KEY_PUBLICKEY);
    String privateKey1 = priAndPubKey.get().get(KEY_PRIVATEKEY);
    System.out.println("公钥:" + publicKey1);
    System.out.println("私钥:" + privateKey1);

  }

  /**
   * 获取公私钥
   *
   * @return
   */
  public static Map<String, String> build() {
    Map<String, Object> map = initKey();
    HashMap<String, String> priAndPubMap = new HashMap<>(2);
    priAndPubMap.put(KEY_PUBLICKEY, new BASE64Encoder().encodeBuffer((byte[]) map.get(KEY_PUBLICKEY)));
    priAndPubMap.put(KEY_PRIVATEKEY, new BASE64Encoder().encodeBuffer((byte[]) map.get(KEY_PRIVATEKEY)));
    return priAndPubMap;
  }


}
